home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / vim_src.zip / TERMLIB.C < prev    next >
C/C++ Source or Header  |  1993-01-12  |  16KB  |  628 lines

  1. /* The following software is (C) 1984 Peter da Silva,
  2.    the Mad Australian, in the public domain. It may
  3.    be re-distributed for any purpose with the inclusion
  4.    of this notice. */
  5. /* modified by Bram Moolenaar */
  6.  
  7. /* TERMLIB: Terminal independant database. */
  8.  
  9. #include "vim.h"
  10. #include "proto/termlib.pro"
  11.  
  12. #ifndef AMIGA
  13. #include <sgtty.h>
  14. #endif
  15.  
  16. static int _match __PARMS((char *, char *));
  17. static char *_addfmt __PARMS((char *, char *, int));
  18. static char *_find __PARMS((char *, char *));
  19.  
  20. /*
  21.  * Global variables for termlib
  22.  */
  23.  
  24. char *tent;                     /* Pointer to terminal entry, set by tgetent */
  25. char PC = 0;                                  /* Pad character, default NULL */
  26. char *UP = 0, *BC = 0;        /* Pointers to UP and BC strings from database */
  27. short ospeed;               /* Baud rate (1-16, 1=300, 16=19200), as in stty */
  28.  
  29. /*
  30.  * Module: tgetent
  31.  *
  32.  * Purpose: Get termcap entry for <term> into buffer at <tbuf>.
  33.  *
  34.  * Calling conventions: char tbuf[1024+], term=canonical name for
  35.  *            terminal.
  36.  *
  37.  * Returned values: 1 = success, -1 = can't open file,
  38.  *            0 = can't find terminal.
  39.  *
  40.  * Notes
  41.  *        Should probably supply static buffer.
  42.  *
  43.  *        Uses environment variables "TERM" and
  44.  *    "TERMCAP". If TERM = term (that is, if the argument
  45.  *    matches the environment) then it looks at TERMCAP.
  46.  *        If TERMCAP begins with a slash, then it assumes
  47.  *    this is the file to search rather than /etc/termcap.
  48.  *        If TERMCAP does not begin with a slash, and it
  49.  *    matches TERM, then this is used as the entry.
  50.  *
  51.  *        This could be simplified considerably for non-UNIX
  52.  *    systems.
  53.  */
  54.  
  55. #ifdef AMIGA
  56. #define TERMCAPFILE "s:termcap"
  57. #else
  58. #define TERMCAPFILE "/etc/termcap"
  59. #endif
  60.  
  61. tgetent(tbuf, term)
  62. char    *tbuf,               /* Buffer to hold termcap entry, 1024 bytes max */
  63.     *term;                                           /* Name of terminal */
  64. {
  65.     char    tcbuf[32],                          /* Temp buffer to handle */
  66.         *tc,                                     /* :tc=: entry for  */
  67.         *tcptr = tcbuf;                          /* extended entries */
  68.     char    *tcap = TERMCAPFILE,              /* Default termcap file */
  69.         *getenv();
  70.     char    *tmp;
  71.     FILE    *termcap;
  72.  
  73.     if((tmp=getenv("TERMCAP")) != NULL) {
  74.         if(*tmp == '/')           /* TERMCAP = name of termcap file */
  75.             tcap = tmp ;
  76.         else {                    /* TERMCAP = termcap entry itself */
  77.             int tlen = strlen(term);
  78.             while(*tmp && *tmp != ':') {/* Check if TERM matches */
  79.                 while(*tmp == '|')
  80.                     tmp++;
  81.                 if(_match(tmp, term)==tlen) {
  82.                     strcpy(tbuf, tmp);
  83.                     tent=tbuf;
  84.                     return 1;
  85.                 } 
  86.                 else
  87.                     tmp = _find(tmp, ":|");
  88.             }
  89.         }
  90.     }
  91.     if(!(termcap=fopen(tcap, "r"))) {
  92.         strcpy(tbuf, tcap);
  93.         return -1;
  94.     }
  95.  
  96.     if(getent(tbuf, term, termcap)) {
  97.         if(tc=tgetstr("tc", &tcptr)) {              /* extended entry */
  98.             rewind(termcap);
  99.             if(getent(tbuf+strlen(tbuf), tc, termcap)) { 
  100.                 fclose(termcap);               /* Completed */
  101.                 return 1; 
  102.             }
  103.             else { 
  104.                 fclose(termcap);              /* Incomplete */
  105.                 return 0; 
  106.             }
  107.         } else { 
  108.             fclose(termcap);              /* non-extended entry */
  109.             return 1; 
  110.         }
  111.     } else { 
  112.         fclose(termcap);                                /* No entry */
  113.         return 0; 
  114.     }
  115. }
  116.  
  117. getent(tbuf, term, termcap)
  118. char *tbuf, *term;
  119. FILE *termcap;
  120. {
  121.     char    *tptr;
  122.     int    tlen = strlen(term);
  123.  
  124.     while(nextent(tbuf, termcap)) {           /* For each possible entry */
  125.         tptr = tbuf;
  126.         while(*tptr && *tptr != ':') {    /* : terminates name field */
  127.             while(*tptr == '|')             /* | seperates names */
  128.                 tptr++;
  129.             if(_match(tptr, term)==tlen) {             /* FOUND! */
  130.                 fclose(termcap);
  131.                 tent=tbuf;
  132.                 return 1;
  133.             } 
  134.             else                           /* Look for next name */
  135.                 tptr = _find(tptr, ":|");
  136.         }
  137.     }
  138.  
  139.     return 0;
  140. }
  141.  
  142. nextent(tbuf, termcap)                     /* Read 1 entry from TERMCAP file */
  143. char    *tbuf;
  144. FILE    *termcap;
  145. {
  146.     char *lbuf =                                     /* lbuf=line buffer */
  147.          tbuf;                        /* read lines straight into buffer */
  148.  
  149.     while(lbuf < tbuf+1024 &&                        /* There's room and */
  150.           fgets(lbuf, (int)(tbuf+1024-lbuf), termcap)) {        /* another line */
  151.         int llen = strlen(lbuf);
  152.  
  153.         if(*lbuf=='#')                               /* eat comments */
  154.             continue;
  155.         if(lbuf[-1]==':' &&                        /* and whitespace */
  156.            lbuf[0]=='\t' &&
  157.            lbuf[1]==':') {
  158.             strcpy(lbuf, lbuf+2);
  159.             llen -= 2;
  160.         }
  161.         if(lbuf[llen-2]=='\\')                  /* and continuations */
  162.             lbuf += llen-2;
  163.         else {
  164.             lbuf[llen-1]=0;           /* no continuation, return */
  165.             return 1;
  166.         }
  167.     }
  168.  
  169.     return 0;                                    /* ran into end of file */
  170. }
  171.  
  172. /*
  173.  * Module: tgetflag
  174.  *
  175.  * Purpose: returns flag true or false as to the existence of a given
  176.  *        entry. used with 'bs', 'am', etc...
  177.  *
  178.  * Calling conventions: id is the 2 character capability id.
  179.  *
  180.  * Returned values: 1 for success, 0 for failure.
  181.  */
  182.  
  183. tgetflag(id)
  184. char *id;
  185. {
  186.     char    buf[256], *ptr = buf;
  187.  
  188.     return tgetstr(id, &ptr) ? 1 : 0;
  189. }
  190.  
  191. /*
  192.  * Module: tgetnum
  193.  *
  194.  * Purpose: get numeric value such as 'li' or 'co' from termcap.
  195.  *
  196.  * Calling conventions: id = 2 character id.
  197.  *
  198.  * Returned values: -1 for failure, else numerical value.
  199.  */
  200.  
  201. tgetnum(id)
  202. char *id;
  203. {
  204.     char *ptr, buf[256];
  205.     ptr = buf;
  206.  
  207.     if(tgetstr(id, &ptr))
  208.         return atoi(buf);
  209.     else
  210.         return 0;
  211. }
  212. /*
  213.  * Module: tgetstr
  214.  *
  215.  * Purpose: get terminal capability string from database.
  216.  *
  217.  * Calling conventions: id is the two character capability id.
  218.  *            (*buf) points into a hold buffer for the
  219.  *            id. the capability is copied into the buffer
  220.  *            and (*buf) is advanced to point to the next
  221.  *            free byte in the buffer.
  222.  *
  223.  * Returned values: 0 = no such entry, otherwise returns original
  224.  *            (*buf) (now a pointer to the string).
  225.  *
  226.  * Notes
  227.  *        It also decodes certain escape sequences in the buffer.
  228.  *    they should be obvious from the code:
  229.  *        \E = escape.
  230.  *        \n, \r, \t, \f, \b match the 'c' escapes.
  231.  *        ^x matches control-x (^@...^_).
  232.  *        \nnn matches nnn octal.
  233.  *        \x, where x is anything else, matches x. I differ
  234.  *    from the standard library here, in that I allow ^: to match
  235.  *    :.
  236.  *
  237.  */
  238.  
  239. char *
  240. tgetstr(id, buf)
  241. char    *id, **buf;
  242. {
  243.     int    len = strlen(id);
  244.     char *tmp=tent;
  245.     char *hold;
  246.  
  247.     do {
  248.         tmp = _find(tmp, ":");                     /* For each field */
  249.         while(*tmp==':')                        /* skip empty fields */
  250.             tmp++;
  251.         if(!*tmp)
  252.             break;
  253.  
  254.         if(_match(id, tmp)==len) {
  255.             tmp += len;                   /* find '=' '@' or '#' */
  256.             if(*tmp=='@')                  /* :xx@: entry for tc */
  257.                 return 0;                   /* deleted entry */
  258.             hold= *buf;
  259.             while(*++tmp && *tmp != ':') {/* not at end of field */
  260.                 switch(*tmp) {
  261.                 case '\\':            /* Expand escapes here */
  262.                     switch(*++tmp) {
  263.                     case 0:        /* ignore backslashes */
  264.                         tmp--;    /* at end of entry */
  265.                         break;   /* shouldn't happen */
  266.                     case 'e':
  267.                     case 'E':                     /* ESC */
  268.                         *(*buf)++ = '\033'; 
  269.                         break;
  270.                     case 'n':                      /* \n */
  271.                         *(*buf)++ = '\n'; 
  272.                         break;
  273.                     case 'r':                      /* \r */
  274.                         *(*buf)++ = '\r'; 
  275.                         break;
  276.                     case 't':                      /* \t */
  277.                         *(*buf)++ = '\t'; 
  278.                         break;
  279.                     case 'b':                      /* \b */
  280.                         *(*buf)++ = '\b'; 
  281.                         break;
  282.                     case 'f':                      /* \f */
  283.                         *(*buf)++ = '\f'; 
  284.                         break;
  285.                     case '0':                    /* \nnn */
  286.                     case '1': 
  287.                     case '2': 
  288.                     case '3': 
  289.                     case '4':
  290.                     case '5': 
  291.                     case '6': 
  292.                     case '7': 
  293.                     case '8': 
  294.                     case '9':
  295.                         **buf = 0;
  296.                         while(isdigit(*tmp))
  297.                             **buf = **buf * 8 + *tmp++ - '0';
  298.                         (*buf)++;
  299.                         tmp--;
  300.                         break;
  301.                     default:      /* \x, for all other x */
  302.                         *(*buf)++= *tmp;
  303.                     }
  304.                     break;
  305.                 case '^':              /* control characters */
  306.                     *(*buf)++ = *++tmp - '@'; 
  307.                     break;
  308.                 default: 
  309.                     *(*buf)++ = *tmp;
  310.                 }
  311.             }
  312.             *(*buf)++ = 0;
  313.             return hold;
  314.         }
  315.     } while(*tmp);
  316.  
  317.     return 0;
  318. }
  319.  
  320. /*
  321.  * Module: tgoto
  322.  *
  323.  * Purpose: decode cm cursor motion string.
  324.  *
  325.  * Calling conventions: cm is cursor motion string.
  326.  *            line, col, are the desired destination.
  327.  *
  328.  * Returned values: a string pointing to the decoded string, or
  329.  *            "OOPS" if it cannot be decoded.
  330.  *
  331.  * Notes
  332.  *        The accepted escapes are:
  333.  *            %d     as in printf, 0 origin.
  334.  *            %2, %3     like %02d, %03d in printf.
  335.  *            %.     like %c
  336.  *            %+x     adds <x> to value, then %.
  337.  *            %>xy     if value>x, adds y. No output.
  338.  *            %i     increments line& col, no output.
  339.  *            %r     reverses order of line&col. No output.
  340.  *            %%     prints as a single %.
  341.  *            %n     exclusive or row & col with 0140.
  342.  *            %B     BCD, no output.
  343.  *            %D     reverse coding (x-2*(x%16)), no output.
  344.  */
  345.  
  346. char *
  347. tgoto(cm, col, line)
  348. char    *cm;                                      /* cm string, from termcap */
  349. int    col,                                           /* column, x position */
  350.     line;                                            /* line, y position */
  351. {
  352.     char    gx, gy,                                           /*    x, y */
  353.         *ptr,                                     /* pointer in 'cm' */
  354.         reverse = 0,                                 /* reverse flag */
  355.         *bufp,                         /* pointer in returned string */
  356.         addup = 0,                                     /* add upline */
  357.         addbak = 0,                                    /* add backup */
  358.         c;
  359.     static char buffer[32];
  360.  
  361.     if(!cm)
  362.         return "OOPS";                       /* Kludge, but standard */
  363.  
  364.     bufp = buffer;
  365.     ptr = cm;
  366.  
  367.     while(*ptr) {
  368.         if((c = *ptr++) != '%') {                     /* normal char */
  369.             *bufp++ = c;
  370.         } else {                                         /* % escape */
  371.             switch(c = *ptr++) {
  372.             case 'd':                                 /* decimal */
  373.                 bufp = _addfmt(bufp, "%d", line);
  374.                 line = col;
  375.                 break;
  376.             case '2':                         /* 2 digit decimal */
  377.                 bufp = _addfmt(bufp, "%02d", line);
  378.                 line = col;
  379.                 break;
  380.             case '3':                         /* 3 digit decimal */
  381.                 bufp = _addfmt(bufp, "%03d", line);
  382.                 line = col;
  383.                 break;
  384.             case '>':                      /* %>xy: if >x, add y */
  385.                 gx = *ptr++;
  386.                 gy = *ptr++;
  387.                 if(col>gx) col += gy;
  388.                 if(line>gx) line += gy;
  389.                 break;
  390.             case '+':                              /* %+c: add c */
  391.                 line += *ptr++;
  392.             case '.':                               /* print x/y */
  393.                 if(line=='\t' ||                /* these are */
  394.                    line == '\n' ||             /* chars that */
  395.                    line=='\004' ||             /* UNIX hates */
  396.                    line=='\0') {
  397.                     line++;         /* so go to next pos */
  398.                     if(reverse==(line==col))
  399.                         addup=1;      /* and mark UP */
  400.                     else
  401.                         addbak=1;           /* or BC */
  402.                 }
  403.                 *bufp++=line;
  404.                 line = col;
  405.                 break;
  406.             case 'r':                              /* r: reverse */
  407.                 gx = line; 
  408.                 line = col; 
  409.                 col = gx;
  410.                 reverse = 1;
  411.                 break;
  412.             case 'i':             /* increment (1-origin screen) */
  413.                 col++;
  414.                 line++;
  415.                 break;
  416.             case '%':                          /* %%=% literally */
  417.                 *bufp++='%';
  418.                 break;
  419.             case 'n':                       /* magic DM2500 code */
  420.                 line ^= 0140;
  421.                 col ^= 0140;
  422.                 break;
  423.             case 'B':                            /* bcd encoding */
  424.                 line = line/10<<4+line%10;
  425.                 col = col/10<<4+col%10;
  426.                 break;
  427.             case 'D':                   /* magic Delta Data code */
  428.                 line = line-2*(line&15);
  429.                 col = col-2*(col&15);
  430.                 break;
  431.             default:                           /* Unknown escape */
  432.                 return "OOPS";
  433.             }
  434.         }
  435.     }
  436.  
  437.     if(addup)                                              /* add upline */
  438.         if(UP) {
  439.             ptr=UP;
  440.             while(isdigit(*ptr) || *ptr=='.')
  441.                 ptr++;
  442.             if(*ptr=='*')
  443.                 ptr++;
  444.             while(*ptr)
  445.                 *bufp++ = *ptr++;
  446.         }
  447.  
  448.     if(addbak)                                          /* add backspace */
  449.         if(BC) {
  450.             ptr=BC;
  451.             while(isdigit(*ptr) || *ptr=='.')
  452.                 ptr++;
  453.             if(*ptr=='*')
  454.                 ptr++;
  455.             while(*ptr)
  456.                 *bufp++ = *ptr++;
  457.         } 
  458.         else
  459.             *bufp++='\b';
  460.  
  461.     *bufp = 0;
  462.  
  463.     return(buffer);
  464. }
  465.  
  466. /*
  467.  * Module: tinit
  468.  *
  469.  * Purpose: simplified terminal initialisation.
  470.  *
  471.  * Calling conventions: name is name of terminal.
  472.  *
  473.  * Returned values: none.
  474.  *
  475.  * Notes
  476.  *        tinit calls tgetent, then sets up the global
  477.  *    variables PC, UP, BC, ospeed appropriately.
  478.  *
  479.  */
  480.  
  481. #if 0        /* already included in term.c */
  482.  
  483. char tbuf[1024];                                /* Buffer for termcap entry */
  484. char junkbuf[1024];                                  /* Big buffer for junk */
  485. char *junkptr;
  486.  
  487. tinit(name)
  488. char *name;
  489. {
  490. #ifndef AMIGA
  491.     struct sgttyb sgbuf;
  492. #endif
  493.     char *ps;
  494.  
  495.     junkptr = junkbuf;
  496.  
  497.     tgetent(tbuf, name);
  498.  
  499.     ps = tgetstr("pc", &junkptr);
  500.     if(ps) PC = *ps;
  501.     UP = tgetstr("up", &junkptr);
  502.     BC = tgetstr("bc", &junkptr);
  503.  
  504. #ifdef AMIGA
  505.     ospeed=0;
  506. #else
  507.     gtty(1, &sgbuf);
  508.     ospeed=sgbuf.sg_ospeed;
  509. #endif
  510.     return 0;
  511. }
  512. #endif
  513.  
  514. /*
  515.  * Module: tputs
  516.  *
  517.  * Purpose: decode padding information
  518.  *
  519.  * Calling conventions: cp = string to be padded, affcnt = # of items
  520.  *            affected (lines, characters, whatever),
  521.  *            outc = routine to output 1 character.
  522.  *
  523.  * Returned values: none
  524.  *
  525.  * Notes
  526.  *        cp has padding information ahead of it, in the form
  527.  *    nnnTEXT or nnn*TEXT. nnn is the number of milliseconds to delay,
  528.  *    and may be a decimal (nnn.mmm). If the asterisk is given, then
  529.  *    the delay is multiplied by afcnt. The delay is produced by outputting
  530.  *    a number of nulls (or other padding char) after printing the
  531.  *    TEXT.
  532.  *
  533.  */
  534.  
  535. long _bauds[16]={
  536.     0,    50,    75,    110,
  537.     134,    150,    200,    300,
  538.     600,    1200,    1800,    2400,
  539.     4800,    9600,    19200,    19200 };
  540.  
  541. tputs(cp, affcnt, outc)
  542. char *cp;                                                 /* string to print */
  543. int affcnt;                                      /* Number of lines affected */
  544. void (*outc) __ARGS((unsigned int));                              /* routine to output 1 character */
  545. {
  546.     long    frac,                    /* 10^(#digits after decimal point) */
  547.         counter,                                           /* digits */
  548.         atol();
  549.  
  550.     if(isdigit(*cp)) {
  551.         counter = 0;
  552.         frac = 1000;
  553.         while(isdigit(*cp))
  554.             counter = counter * 10L + (long)(*cp++ - '0');
  555.         if(*cp=='.')
  556.             while(isdigit(*++cp)) {
  557.                 counter = counter * 10L + (long)(*cp++ - '0');
  558.                 frac = frac * 10;
  559.             }
  560.         if(*cp!='*') {                 /* multiply by affected lines */
  561.             if(affcnt>1) affcnt = 1;
  562.         } 
  563.         else
  564.             cp++;
  565.  
  566.         /* Calculate number of characters for padding counter/frac ms delay */
  567.         if(ospeed)
  568.             counter = (counter * _bauds[ospeed] * (long)affcnt) / frac;
  569.  
  570.         while(*cp)                                  /* output string */
  571.             (*outc)(*cp++);
  572.         if(ospeed)
  573.             while(counter--)            /* followed by pad characters */
  574.                 (*outc)(PC);
  575.     } 
  576.     else
  577.         while(*cp)
  578.             (*outc)(*cp++);
  579.     return 0;
  580. }
  581.  
  582. /*
  583.  * Module: tutil.c
  584.  *
  585.  * Purpose: Utility routines for TERMLIB functions.
  586.  *
  587.  */
  588.  
  589.     static int
  590. _match(s1, s2)                 /* returns length of text common to s1 and s2 */
  591. char *s1, *s2;
  592. {
  593.     int i = 0;
  594.  
  595.     while(s1[i] && s1[i] == s2[i])
  596.         i++;
  597.  
  598.     return i;
  599. }
  600.  
  601.     static char *
  602. _find(s, set)   /* finds next c in s that's a member of set, returns pointer */
  603. char *s, *set;
  604. {
  605.     for(; *s; s++) {
  606.         char    *ptr = set;
  607.  
  608.         while(*ptr && *s != *ptr)
  609.             ptr++;
  610.  
  611.         if(*ptr)
  612.             return s;
  613.     }
  614.  
  615.     return s;
  616. }
  617.  
  618.     static char *
  619. _addfmt(buf, fmt, val)             /* add val to buf according to format fmt */
  620. char *buf, *fmt;
  621. int val;
  622. {
  623.     sprintf(buf, fmt, val);
  624.     while(*buf)
  625.         buf++;
  626.     return buf;
  627. }
  628.